# -*- coding: utf-8 -*-
# created on 2016/12/8
from sympy import factor, S
from mathsolver.functions.base.base import BaseFunction, new_latex
from mathsolver.functions.base.objects import BaseBelong, BaseIneq, BaseFunc, BaseEq
from mathsolver.functions.sympy_utils import default_symbol
from mathsolver.functions.hanshu.helper import eval_sign, reverse_op
from mathsolver.functions.hanshu.dingyiyu import QiuDingYiYu


class CanBianFenLi(BaseFunction):
    """参变分离：一个变量，一个参数
    如果某个符号的范围给定，则这个符号为变量，否则默认 x, y, z 为变量
    变量系数含有参数，若系数范围恒大于或小于0，则可以参变分离；否则不能分离
    """

    def solver(self, *args):
        ineq = args[0]
        left, op, right = ineq.sympify()
        expr = left - right  # expr op 0

        # 根据输入求出变量、参数
        if len(args) > 1:
            belong = args[-1]
            var, interval = belong.var, belong.interval
        else:
            var = default_symbol(expr)
            # 求定义域
            try:
                interval = QiuDingYiYu().solver(BaseEq(['y', expr]))
            except Exception:
                interval = S.Reals

            belong = BaseBelong([var, '\\in', interval])
        parameter = expr.free_symbols.difference({var}).pop()

        # 分离变量、参数
        # assert expr.func is Add
        noparam_part, param_part = expr.expand().as_independent(parameter)

        # assert factor(param_part).func is Mul
        param_part_xishu, param_part_param = factor(param_part).as_independent(parameter)

        # 判断是否可以参变分离，若无法分离，输出 f(x, a) op 0
        if param_part_param.has(var):
            # raise AttributeError("无法参变分离 %s" % ineq.printing())
            res = BaseIneq([left - right, op, 0])
        else:
            # 如果可以分离，求出参数前面系数的符号，如果恒大于或小于0，则分离参数
            try:
                xishu_sign = eval_sign(param_part_xishu, var, interval)
            except NotImplementedError:
                res = BaseIneq([left - right, op, 0])
            else:
                if abs(xishu_sign) == 2:
                    self.steps.append(["", "当 %s = 0 时，不等式恒成立，所以只要考虑 %s ≠ 0"
                                       % (new_latex(param_part_xishu), new_latex(param_part_xishu))])

                op = reverse_op(op) if xishu_sign < 0 else op
                g_x = noparam_part / param_part_xishu
                h_a = - param_part_param
                res = BaseIneq([g_x, op, h_a])

        self.label.add("参变分离")
        self.steps.append(["", "参变分离得：%s" % res.printing()])
        self.output.extend([res, belong])
        return self


class XxXingDingYi(BaseFunction):
    """ 'xx'型定义 """
    op_group1 = {'>', '>='}
    op_group2 = {'<', '<='}

    @staticmethod
    def input_chuli(args):
        """处理输入，根据输入求出变量、参数"""
        if len(args) == 2:
            ineq, belong = args
            left, op, right = ineq.sympify()

            # belong 是 BaseBelong 或者 BaseBelongs
            var, interval = belong.var, belong.interval
        elif len(args) == 3:
            ineq, b1, b2 = args
            left, op, right = ineq.sympify()
            var, interval = (b1.var, b2.var), (b1.interval, b2.interval)
        else:
            raise ValueError("don't know how to handle input %s" % args)

        return interval, left, op, right, var


class FxMin:
    """f(x)_min"""

    def __init__(self, basefunc_obj, def_interval):
        self.BaseFunc_obj = basefunc_obj
        self.def_interval = def_interval

    def __str__(self):
        func = self.BaseFunc_obj
        f_name, f_var = func.name, func.var
        return "%s(%s)_{min}" % (f_name, f_var)


class FxMax:
    """f(x)_max"""

    def __init__(self, basefunc_obj, def_interval):
        self.BaseFunc_obj = basefunc_obj
        self.def_interval = def_interval

    def __str__(self):
        func = self.BaseFunc_obj
        f_name, f_var = func.name, func.var
        return "%s(%s)_{max}" % (f_name, f_var)


class RenYiXingDingYi(XxXingDingYi):
    """hs030.“任意”型定义
    输入的不等式为参变分离后的形式"""

    def solver(self, *args):
        interval, left, op, right, var = self.input_chuli(*args)
        func = BaseFunc({"name": "f", "var": var, "expression": left, "type": ""})

        if op in self.op_group1:
            left_eval = FxMin(func, interval)
        else:
            left_eval = FxMax(func, interval)

        self.steps.append(["", "根据不等式恒成立条件可知 %s %s %s" % (left_eval, op, new_latex(right))])
        self.label.add("“任意”型定义")
        self.output.append(BaseIneq([left_eval, op, right]))
        return self


class CunZaiXingDingYi(XxXingDingYi):
    """hs032.“存在”型定义
    输入的不等式为参变分离后的形式
    """

    def solver(self, *args):
        interval, left, op, right, var = self.input_chuli(*args)
        func = BaseFunc({"name": "f", "var": var, "expression": left, "type": ""})

        if op in self.op_group1:
            left_eval = FxMax(func, interval)
        else:
            left_eval = FxMin(func, interval)

        self.steps.append(["", "根据不等式有解条件可知 %s %s %s" % (left_eval, op, new_latex(right))])
        self.label.add("“存在”型定义")
        self.output.append(BaseIneq([left_eval, op, right]))
        return self


class RenYiRenYiXingDingYi(XxXingDingYi):
    """hs033.“任意≥任意”型定义"""

    def solver(self, *args):
        interval, left, op, right, (var1, var2) = self.input_chuli(*args)

        func1 = BaseFunc({"name": "f", "var": var1, "expression": left, "type": ""})
        func2 = BaseFunc({"name": "f", "var": var2, "expression": right, "type": ""})

        if isinstance(interval, (tuple, list)):
            interval1, interval2 = interval
        else:
            interval1 = interval
            interval2 = interval

        if op in self.op_group1:
            left_eval = FxMin(func1, interval1)
            right_eval = FxMax(func2, interval2)
        else:
            left_eval = FxMax(func1, interval1)
            right_eval = FxMin(func2, interval2)

        self.steps.append(["", "根据不等式恒成立条件可知 %s %s %s" % (left_eval, op, right_eval)])
        self.label.add("“任意≥任意”型定义")
        self.output.append(BaseIneq([left_eval, op, right_eval]))
        return self


class CunZaiCunZaiXingDingYi(XxXingDingYi):
    """hs034.“存在≥存在”型定义"""

    def solver(self, *args):
        interval, left, op, right, (var1, var2) = self.input_chuli(*args)
        func1 = BaseFunc({"name": "f", "var": var1, "expression": left, "type": ""})
        func2 = BaseFunc({"name": "f", "var": var2, "expression": right, "type": ""})

        if isinstance(interval, tuple):
            interval1, interval2 = interval
        else:
            interval1 = interval
            interval2 = interval

        if op in self.op_group1:
            left_eval = FxMax(func1, interval1)
            right_eval = FxMin(func2, interval2)
        else:
            left_eval = FxMin(func1, interval1)
            right_eval = FxMax(func2, interval2)

        self.steps.append(["", "根据条件可知 %s %s %s" % (left_eval, op, right_eval)])
        self.label.add("“存在≥存在”型定义")
        self.output.append(BaseIneq([left_eval, op, right_eval]))
        return self


class RenYiCunZaiXingDingYi(XxXingDingYi):
    """hs035.“任意≥（≤）存在”型定义"""

    def solver(self, *args):
        interval, left, op, right, (var1, var2) = self.input_chuli(*args)
        func1 = BaseFunc({"name": "f", "var": var1, "expression": left, "type": ""})
        func2 = BaseFunc({"name": "f", "var": var2, "expression": right, "type": ""})

        if isinstance(interval, tuple):
            interval1, interval2 = interval
        else:
            interval1 = interval
            interval2 = interval

        if op in self.op_group1:
            left_eval = FxMin(func1, interval1)
            right_eval = FxMin(func2, interval2)
        else:
            left_eval = FxMax(func1, interval1)
            right_eval = FxMax(func2, interval2)

        self.steps.append(["", "根据条件可知 %s %s %s" % (left_eval, op, right_eval)])
        self.label.add("“任意≥（≤）存在”型定义")
        self.output.append(BaseIneq([left_eval, op, right_eval]))
        return self


if __name__ == '__main__':
    pass
